SiteTooltip   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 149
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 72
dl 0
loc 149
rs 10
c 0
b 0
f 0
wmc 16

5 Functions

Rating   Name   Duplication   Size   Complexity  
A constructor 0 13 1
A create 0 19 3
A positionTooltip 0 23 3
B showTooltip 0 52 6
A hideTooltip 0 13 3
1
/*!
2
 * @package   ElkArte Forum
3
 * @copyright ElkArte Forum contributors
4
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
5
 *
6
 * @version 2.0 dev
7
 */
8
9
/**
10
 * SiteTooltip, Basic JavaScript function to provide styled tooltips
11
 *
12
 * - shows the tooltip in a div with the class defined in tooltipClass
13
 * - moves all selector titles to a hidden div and removes the title attribute to
14
 *   prevent default browser actions
15
 * - attempts to keep the tooltip on screen, prefers it "on top" but will move it below if there
16
 * is not enough screen room above
17
 *
18
 */
19
class SiteTooltip
20
{
21
	constructor (settings = {})
22
	{
23
		this.defaults = {
24
			tooltipID: 'site_tooltip', // ID used on the outer div
25
			tooltipTextID: 'site_tooltipText', // ID on the inner div holding the text
26
			tooltipClass: 'tooltip', // The class applied to the sibling span, defaults provides a fade cover
27
			tooltipSwapClass: 'site_swaptip', // a class only used internally, change only if you have a conflict
28
			tooltipContent: 'html' // display captured title text as html or text
29
		};
30
31
		// Account for any user options
32
		this.settings = Object.assign({}, this.defaults, settings);
33
	}
34
35
	/**
36
	 * Creates tooltips for elements.
37
	 *
38
	 * @param {string} elem - The CSS selector of the elements to create tooltips for.
39
	 *
40
	 * @returns {null} - Returns null if the device is mobile or touch-enabled.
41
	 */
42
	create (elem)
43
	{
44
		// No need here
45
		if (is_mobile === true || is_touch === true)
0 ignored issues
show
Bug introduced by
The variable is_mobile seems to be never declared. If this is a global, consider adding a /** global: is_mobile */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
Bug introduced by
The variable is_touch seems to be never declared. If this is a global, consider adding a /** global: is_touch */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
46
		{
47
			return null;
48
		}
49
50
		// Move passed selector titles to a hidden span, then remove the selector title to prevent any default browser actions
51
		for (let el of document.querySelectorAll(elem))
52
		{
53
			let title = el.getAttribute('title');
54
55
			el.removeAttribute('title');
56
			el.setAttribute('data-title', title);
57
			el.addEventListener('mouseenter', this.showTooltip.bind(this));
58
			el.addEventListener('mouseleave', this.hideTooltip.bind(this));
59
		}
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
60
	}
61
62
	/**
63
	 * Positions the tooltip element relative to the provided event target.
64
	 *
65
	 * @param {Event} event - The event object that triggered the tooltip placement.
66
	 */
67
	positionTooltip (event)
68
	{
69
		let tooltip = document.getElementById(this.settings.tooltipID);
70
		if (!tooltip)
71
		{
72
			return;
73
		}
74
75
		let rect = event.target.getBoundingClientRect();
76
		let tooltipHeight = tooltip.offsetHeight;
77
78
		let x = rect.left;
79
		// Initially trying to position above the element
80
		let y = window.scrollY + rect.top - tooltipHeight - 5;
81
82
		// Don't position above if it falls off-screen, instead move it below
83
		if (rect.top - (tooltipHeight + 5) < 0)
84
		{
85
			y = window.scrollY + rect.bottom + 5;
86
		}
87
88
		tooltip.style.cssText = 'left:' + x + 'px; top:' + y + 'px';
89
	}
90
91
	/**
92
	 * Displays a tooltip on hover over an element.
93
	 *
94
	 * @param {Event} event - The event object.
95
	 */
96
	showTooltip (event)
97
	{
98
		if (this.tooltipTimeout)
99
		{
100
			clearTimeout(this.tooltipTimeout);
101
		}
102
103
		// Represents the timeout for showing a tooltip.
104
		this.tooltipTimeout = setTimeout(function() {
105
			let title = event.target.getAttribute('data-title');
106
			if (title)
107
			{
108
				// <div id="site_tooltip"><div id="site_tooltipText"><span class="tooltip"
109
				let tooltip = document.createElement('div');
110
				tooltip.id = this.settings.tooltipID;
111
112
				let tooltipText = document.createElement('div');
113
				tooltipText.id = this.settings.tooltipTextID;
114
115
				let span = document.createElement('span');
116
				span.className = this.settings.tooltipClass;
117
118
				// Create our element and append it to the body.
119
				tooltip.appendChild(tooltipText);
120
				tooltip.appendChild(span);
121
				document.getElementsByTagName('body')[0].appendChild(tooltip);
122
123
				// Load the tooltip content with our data-title
124
				if (this.settings.tooltipContent === 'html')
125
				{
126
					// Regular expression to match content inside .bbc_code_inline span
127
					let regex = new RegExp('(<span class="bbc_code_inline">).*?(<\/span>)', 's');
128
129
					title = title.replace(regex, function(match, p1, p2)
130
					{
131
						let content = match.slice(p1.length, -p2.length);
132
						let replacedContent = content.replace(/</g, "&lt;");
133
						return p1 + replacedContent + p2;
134
					});
135
136
					tooltipText.innerHTML = title;
137
				}
138
				else
139
				{
140
					tooltipText.innerText = title;
141
				}
142
143
				tooltip.style.display = 'block';
144
				this.positionTooltip(event);
145
			}
146
		}.bind(this), 1000);
147
	}
148
149
	/**
150
	 * Hides the tooltip.
151
	 *
152
	 * @param {Event} event - The event object.
153
	 */
154
	hideTooltip (event)
0 ignored issues
show
Unused Code introduced by
The parameter event is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
155
	{
156
		if (this.tooltipTimeout)
157
		{
158
			clearTimeout(this.tooltipTimeout);
159
		}
160
161
		let tooltip = document.getElementById(this.settings.tooltipID);
162
		if (tooltip)
163
		{
164
			tooltip.parentElement.removeChild(tooltip);
165
		}
166
	}
167
}
168
169
window.SiteTooltip = SiteTooltip;
170